using System;
using System.Collections.Generic;
using Animancer;
using MoonFramework.Scripts.Tool;
using MoonFramework.Template;
using UnityEngine;

namespace MoonFramework.View
{
    public class AnimancerManager : BaseManager<AnimancerManager>
    {
        private readonly Dictionary<string, Dictionary<string, ClipTransition>> _animations = new();
        private readonly Dictionary<string, Dictionary<string, int>> _hashToInts = new();
        public readonly Dictionary<string, LinearMixerState> States = new();
        private int _idCounter;

        public void Play(ClipTransition clipTransition, AnimancerComponent animancerComponent)
        {
            animancerComponent.Play(clipTransition);
        }
        
        /// <summary>
        ///     Allocates a new LinearMixerState for the given name if it doesn't already exist.
        /// </summary>
        public void Allocate(string name)
        {
            if (!States.ContainsKey(name))
                States[name] = this.MoonObjGetPool(() => new LinearMixerState());
        }

        /// <summary>
        ///     Adds a ClipTransition to a LinearMixerState and registers its unique ID.
        /// </summary>
        public void Add(string name, string animationName, ClipTransition animation)
        {
            if (!_animations.ContainsKey(name))
                _animations[name] = new Dictionary<string, ClipTransition>();

            if (!_hashToInts.ContainsKey(name))
                _hashToInts[name] = new Dictionary<string, int>();

            if (_animations[name].TryAdd(animationName, animation))
            {
                if (States.TryGetValue(name, out var state))
                {
                    state.Add(animation, ++_idCounter);
                    _hashToInts[name][animationName] = _idCounter;
                }
            }
        }

        /// <summary>
        ///     Plays an animation by setting the corresponding parameter in the LinearMixerState.
        /// </summary>
        public void Play(string name, string animationName)
        {
            if (States.TryGetValue(name, out var state) &&
                _hashToInts.TryGetValue(name, out var animDict) &&
                animDict.TryGetValue(animationName, out var id))
            {
                state.Parameter = id;
            }
        }

        /// <summary>
        ///     Stops all animations associated with the given LinearMixerState.
        /// </summary>
        public void Stop(string name)
        {
            if (States.TryGetValue(name, out var state))
            {
                state.Parameter = 0;
            }
        }

        /// <summary>
        ///     Transitions smoothly from the current animation to the specified animation.
        /// </summary>
        public void SmoothPlay(string name, string animationName, float fadeDuration = 0.25f)
        {
            if (States.TryGetValue(name, out var state) &&
                _hashToInts.TryGetValue(name, out var animDict) &&
                animDict.TryGetValue(animationName, out var id))
            {
                state.StartFade(id, fadeDuration);
            }
        }

        /// <summary>
        ///     Checks if the given animation is currently playing.
        /// </summary>
        public bool IsPlaying(string name, string animationName)
        {
            if (States.TryGetValue(name, out var state) &&
                _hashToInts.TryGetValue(name, out var animDict) &&
                animDict.TryGetValue(animationName, out var id))
            {
                return Mathf.Approximately(state.Parameter, id);
            }

            return false;
        }

        /// <summary>
        ///     Removes an animation from a LinearMixerState and its corresponding ID mapping.
        /// </summary>
        public void Remove(string name, string animationName, bool destroy = false)
        {
            if (States.TryGetValue(name, out var state) &&
                _hashToInts.TryGetValue(name, out var animDict) &&
                animDict.TryGetValue(animationName, out var id))
            {
                state.Remove(id, destroy);
                animDict.Remove(animationName);
                _animations[name].Remove(animationName);

                if (_animations[name].Count == 0)
                {
                    _animations.Remove(name);
                }

                if (_hashToInts[name].Count == 0)
                {
                    _hashToInts.Remove(name);
                }
            }
        }

        /// <summary>
        ///     Clears all animations from the specified LinearMixerState.
        /// </summary>
        public void ClearAnimations(string name)
        {
            if (States.TryGetValue(name, out var state))
            {
                state.MoonObjPushPool();
                States.Remove(name);
            }

            if (_animations.ContainsKey(name))
            {
                _animations[name].Clear();
                _animations.Remove(name);
            }

            if (_hashToInts.ContainsKey(name))
            {
                _hashToInts[name].Clear();
                _hashToInts.Remove(name);
            }
        }
    }
}